

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/favicon.png">
  <link rel="icon" href="/img/favicon.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="MiaoHao-oops">
  <meta name="keywords" content="">
  
    <meta name="description" content="chisel 是一门新兴的敏捷硬件描述语言，在使用了“几天”后，我打算写这篇评测。">
<meta property="og:type" content="article">
<meta property="og:title" content="chisel 使用浅测">
<meta property="og:url" content="http://miaohao-oops.github.io/2022/06/02/chisel%E4%BD%BF%E7%94%A8%E6%B5%85%E6%B5%8B/index.html">
<meta property="og:site_name" content="Miao&#39;s Blog">
<meta property="og:description" content="chisel 是一门新兴的敏捷硬件描述语言，在使用了“几天”后，我打算写这篇评测。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://miaohao-oops.github.io/img/chisel_type_hierarchy.svg">
<meta property="article:published_time" content="2022-06-02T00:58:59.000Z">
<meta property="article:modified_time" content="2023-10-25T04:38:40.119Z">
<meta property="article:author" content="MiaoHao-oops">
<meta property="article:tag" content="硬件描述语言">
<meta property="article:tag" content="chisel">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="http://miaohao-oops.github.io/img/chisel_type_hierarchy.svg">
  
  
  <title>chisel 使用浅测 - Miao&#39;s Blog</title>

  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4/dist/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@4/github-markdown.min.css" />
  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hint.css@2/hint.min.css" />

  
    
    
      
      <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@10/styles/github-gist.min.css" />
    
  

  
    <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3/dist/jquery.fancybox.min.css" />
  


<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_ba1fz6golrf.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/css/main.css" />

<!-- 自定义样式保持在最底部 -->


  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    var CONFIG = {"hostname":"miaohao-oops.github.io","root":"/","version":"1.8.14","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"right","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"copy_btn":true,"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"appId":"y7tlaLEOoW5C1xBYm8VBo64Q-gzGzoHsz","appKey":"F7gylUefEEtQk24FEbvgfnBi","server_url":"https://y7tlaleo.lc-cn-n1-shared.com","path":"window.location.pathname","ignore_local":true}},"search_path":"/local-search.xml"};
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
<meta name="generator" content="Hexo 6.0.0"><link rel="alternate" href="/atom.xml" title="Miao's Blog" type="application/atom+xml">
</head>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/">
      <strong>MiaoHao-oops</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="banner" id="banner" parallax=true
         style="background: url('/img/default.png') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="page-header text-center fade-in-up">
            <span class="h2" id="subtitle" title="chisel 使用浅测">
              
            </span>

            
              <div class="mt-3">
  
  
    <span class="post-meta">
      <i class="iconfont icon-date-fill" aria-hidden="true"></i>
      <time datetime="2022-06-02 08:58" pubdate>
        2022年6月2日 早上
      </time>
    </span>
  
</div>

<div class="mt-1">
  
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      7.2k 字
    </span>
  

  
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      
      61 分钟
    </span>
  

  
  
    
      <!-- LeanCloud 统计文章PV -->
      <span id="leancloud-page-views-container" class="post-meta" style="display: none">
        <i class="iconfont icon-eye" aria-hidden="true"></i>
        <span id="leancloud-page-views"></span> 次
      </span>
    
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div class="py-5" id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">chisel 使用浅测</h1>
            
            <div class="markdown-body">
              <h2 id="什么是-chisel">什么是 chisel</h2>
<p>关于“什么是
chisel”这个问题，在网上已经有了详尽的讨论：它是硬件描述语言，它是 scala
的包等等。在我的使用范畴下，我认为它是一个编译器，依托于 scala
包的形式存在，有些类似于 LLVM
以库的形式存在的特点。它具有编译器的特点：源语言是 scala，目标语言是
verilog，此外还具有中间表示 FIRRTL。chisel 的工作过程可以理解为：通过在
scala 代码中调用一些 chisel 包中的类和方法，再运行这段 scala
代码，产生可综合的 verilog。</p>
<h2 id="chisel-的优势">chisel 的优势</h2>
<p>chisel 的敏捷性源于它更高层次的抽象以及 scala
语言本身面向对象、函数式的特点。</p>
<h3 id="丰富的数据类型">丰富的数据类型</h3>
<p>下面是 <a target="_blank" rel="noopener" href="https://www.chisel-lang.org/">chisel 官网</a>
中数据类型的继承关系图：</p>
<p><img src="/img/chisel_type_hierarchy.svg" srcset="/img/loading.gif" lazyload /></p>
<p>比较常用的数据类型有
<code>UInt</code>，<code>SInt</code>，<code>Bool</code>，<code>Clock</code>
和 <code>Reset</code>。此外还可以通过 <code>Vec</code> 和
<code>Bundle</code> 将它们聚集起来，<code>Vec</code>
用于聚集相同的类型，而 <code>Bundle</code> 用于聚集不同的类型。</p>
<h3 id="简单的硬件模型">简单的硬件模型</h3>
<p>在 chisel 中的硬件模型只有两种：线网 <code>Wire</code> 和寄存器
<code>Reg</code>，并且使用了 <code>Reg</code>
声明的变量最终一定会产生寄存器，而不像 verilog 一样不能确定。</p>
<p>声明一个硬件需要将数据类型和硬件模型结合起来使用：</p>
<figure class="highlight scala"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></div></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">val</span> foo = <span class="hljs-type">Wire</span>(<span class="hljs-type">UInt</span>(<span class="hljs-number">32.</span><span class="hljs-type">W</span>))<br><span class="hljs-keyword">val</span> bar = <span class="hljs-type">Reg</span>(<span class="hljs-type">Bool</span>())<br></code></pre></td></tr></table></figure>
<p><code>foo</code> 描述了一个 32 位宽的线，<code>bar</code> 描述了一个
1 位宽的寄存器。需要注意的是，在 chisel
中的时钟和复位信号是隐含的，<code>Reg</code>
默认使用该寄存器所在模块的同步时钟和复位信号。</p>
<h3 id="自定义类型">自定义类型</h3>
<p>在编写 verilog
时，常常因为散乱的接口而难以维护，陷入“设计一小时，模块编码一小时，接线两小时”的窘境，chisel
的自定义类型解决了这一问题。</p>
<h4 id="bundle-继承">Bundle 继承</h4>
<p>通过继承 <code>Bundle</code>
类，用户可以自定义线束类型，将不同类型的线聚集起来：</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyBundle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Bundle</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> src = <span class="hljs-type">Vec</span>(<span class="hljs-number">2</span>, <span class="hljs-type">UInt</span>(<span class="hljs-number">32.</span><span class="hljs-type">W</span>))<br>  <span class="hljs-keyword">val</span> op = <span class="hljs-type">Vec</span>(<span class="hljs-number">12</span>, <span class="hljs-type">Bool</span>())<br>&#125;<br></code></pre></td></tr></table></figure>
<p>定义了由两个 32 位操作数 <code>src</code> 和一个 12 位操作码
<code>op</code>
所组成的线束。在模块中，可以使用自定义类型产生硬件并对其进行连接：</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">val</span> myBundle = <span class="hljs-type">Wire</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">MyBundle</span>)<br>myBundle.src(<span class="hljs-number">0</span>) := <span class="hljs-number">0.</span><span class="hljs-type">U</span><br>myBundle.src(<span class="hljs-number">1</span>) := <span class="hljs-number">1.</span><span class="hljs-type">U</span><br>myBundle.op := <span class="hljs-string">&quot;b000100&quot;</span>.<span class="hljs-type">U</span><br></code></pre></td></tr></table></figure>
<h4 id="module-继承">Module 继承</h4>
<p>Module 是电路中基本组成单元，chisel 中的模块同样抽象为 IO
接口和内部逻辑，通过继承 Module 可以自定义模块：</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyModule</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Module</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> io = <span class="hljs-type">IO</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">Bundle</span>()&#123;<br>    <span class="hljs-keyword">val</span> in = <span class="hljs-type">Input</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">MyBundle</span>)<br>    <span class="hljs-keyword">val</span> out = <span class="hljs-type">Output</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">Bundle</span>()&#123;<br>      <span class="hljs-keyword">val</span> res = <span class="hljs-type">UInt</span>(<span class="hljs-number">32.</span><span class="hljs-type">W</span>)<br>    &#125;)<br>  &#125;)<br><br>  io.out.res := <span class="hljs-type">Mux1H</span>(<span class="hljs-type">Seq</span>(<br>    io.in.op(<span class="hljs-number">0</span>) -&gt; io.in.src(<span class="hljs-number">0</span>) + io.in.src(<span class="hljs-number">1</span>),<br>    io.in.op(<span class="hljs-number">1</span>) -&gt; io.in.src(<span class="hljs-number">0</span>) - io.in.src(<span class="hljs-number">1</span>),<br>    ...<br>  ))<br>&#125;<br></code></pre></td></tr></table></figure>
<p>定义了一个使用 12 位独热码作为操作码的 alu 模块。其中值
<code>io</code> 定义了该模块的 IO 接口。为了创建它，定义了一个匿名的
Bundle 对象，其中包含 <code>in</code> 和 <code>out</code>
分别定义输入接口和输出接口。定义 IO 接口中的 3 个方法
<code>IO</code>，<code>Input</code> 和 <code>Output</code>
接受的参数都是 <code>Bundle</code>
的对象，因此在这里可以使用自定义的线束类型。<code>MyModule</code>
中剩余部分是该模块的内部逻辑，这里使用了原语 <code>Mux1H</code>
生成一个独热码选择器。</p>
<h4 id="快速连接模块">快速连接模块</h4>
<p>chisel 中基本的连接算符是 <code>:=</code>
，信号的流动方向是从右向左。chisel 中还提供了一种 element-wise
的连接算符
<code>&lt;&gt;</code>，它可以快速连接线束中的<strong>同名元素</strong>，并检查其类型是否相同：</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyModuleWrap</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Module</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> io = <span class="hljs-type">IO</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">Bundle</span>()&#123;<br>    <span class="hljs-keyword">val</span> in = <span class="hljs-type">Input</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">MyBundle</span>)<br>    <span class="hljs-keyword">val</span> out = <span class="hljs-type">Output</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">Bundle</span>()&#123;<br>      <span class="hljs-keyword">val</span> res = <span class="hljs-type">UInt</span>(<span class="hljs-number">32.</span><span class="hljs-type">W</span>)<br>    &#125;)<br>  &#125;)<br>  <span class="hljs-keyword">val</span> myAlu = <span class="hljs-type">Module</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">MyModule</span>)<br><br>  myAlu.io&lt;&gt;io<br>&#125;<br></code></pre></td></tr></table></figure>
<h3 id="强大的语法检查">强大的语法检查</h3>
<p>正如上文所提到的，在 chisel
进行编译时，编译器将检查连接左右硬件的数据类型是否相同，若不同则报错，停止编译。实际上，chisel
编译器还做了更多的事情，能够帮助在编码时就检查出一些设计功能无关的错误，节省开发时间。</p>
<h3 id="参数化模块">参数化模块</h3>
<p>由于所有类型的定义都是基于 scala
的类，因此可以使用类的初始化参数对模块进行参数化定义：</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyBundle</span>(<span class="hljs-params">xLen:<span class="hljs-type">Int</span> = 32</span>) <span class="hljs-keyword">extends</span> <span class="hljs-title">Bundle</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> src = <span class="hljs-type">Vec</span>(<span class="hljs-number">2</span>, <span class="hljs-type">UInt</span>(xLen.<span class="hljs-type">W</span>))<br>  <span class="hljs-keyword">val</span> op = <span class="hljs-type">Vec</span>(<span class="hljs-number">12</span>, <span class="hljs-type">Bool</span>())<br>&#125;<br><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyModule</span>(<span class="hljs-params">xLen:<span class="hljs-type">Int</span> = 32</span>) <span class="hljs-keyword">extends</span> <span class="hljs-title">Module</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> io = <span class="hljs-type">IO</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">Bundle</span>()&#123;<br>    <span class="hljs-keyword">val</span> in = <span class="hljs-type">Input</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">MyBundle</span>(xLen))<br>    <span class="hljs-keyword">val</span> out = <span class="hljs-type">Output</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">Bundle</span>()&#123;<br>      <span class="hljs-keyword">val</span> res = <span class="hljs-type">UInt</span>(xLen.<span class="hljs-type">W</span>)<br>    &#125;)<br>  &#125;)<br><br>  io.out.res := <span class="hljs-type">Mux1H</span>(<span class="hljs-type">Seq</span>(<br>    io.in.op(<span class="hljs-number">0</span>) -&gt; io.in.src(<span class="hljs-number">0</span>) + io.in.src(<span class="hljs-number">1</span>),<br>    io.in.op(<span class="hljs-number">1</span>) -&gt; io.in.src(<span class="hljs-number">0</span>) - io.in.src(<span class="hljs-number">1</span>),<br>    ...<br>  ))<br>&#125;<br></code></pre></td></tr></table></figure>
<p>在实例化模块时，可以通过改变传入的参数定制不同的模块：</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">val</span> myModule1 = <span class="hljs-type">Module</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">MyModule</span>(<span class="hljs-number">16</span>))<br><span class="hljs-keyword">val</span> myModule2 = <span class="hljs-type">Module</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">MyModule</span>(<span class="hljs-number">32</span>))<br></code></pre></td></tr></table></figure>
<h3 id="scala-集合类型和函数式编程">scala 集合类型和函数式编程</h3>
<p>集合类型和函数式编程都是 scala 的语言特性，由于 chisel 是 scala
的包，因此可以使用这些特性用于构造硬件生成器。</p>
<p>比如说对于设计 CPU
核而言，一个明显的集合是<strong>指令集</strong>，在 chisel
中，可以将所有指令放到一个查找表中，利用 scala 的
<code>ListLookUp</code> 方法生成译码逻辑：</p>
<ol type="1">
<li>首先，构造查找表，它使用一个指令作为索引，查找结果是控制信号：</li>
</ol>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">DecodeConstants</span> </span>&#123;<br>  <span class="hljs-class"><span class="hljs-keyword">object</span> <span class="hljs-title">CtrlIdx</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">scala</span>.<span class="hljs-title">Enumeration</span> </span>&#123;<br>    <span class="hljs-keyword">val</span> selSrc0, selSrc1, selImm, selWnum, fuType, fuOp, rfWen, illegal = <span class="hljs-type">Value</span><br>  &#125;<br><br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">default</span></span>: <span class="hljs-type">List</span>[<span class="hljs-type">UInt</span>] = <span class="hljs-comment">// no instruction</span><br>  <span class="hljs-comment">//     SelSrc(0)</span><br>  <span class="hljs-comment">//     |   SelSrc(1)</span><br>  <span class="hljs-comment">//     |   |   SelImm</span><br>  <span class="hljs-comment">//     |   |   |    SelWnum</span><br>  <span class="hljs-comment">//     |   |   |    |      FuType</span><br>  <span class="hljs-comment">//     |   |   |    |      |    FuOp</span><br>  <span class="hljs-comment">//     |   |   |    |      |    |    rfWen</span><br>  <span class="hljs-comment">//     |   |   |    |      |    |    |  illegal</span><br>    <span class="hljs-type">List</span>(rs, rt, nan, dzero, alu, sll, f, t)<br>  <span class="hljs-keyword">val</span> table: <span class="hljs-type">Array</span>[(<span class="hljs-type">BitPat</span>, <span class="hljs-type">List</span>[<span class="hljs-type">UInt</span>])]<br>&#125;<br><br><span class="hljs-class"><span class="hljs-keyword">object</span> <span class="hljs-title">MIPS32I</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DecodeConstants</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> table: <span class="hljs-type">Array</span>[(<span class="hljs-type">BitPat</span>, <span class="hljs-type">List</span>[<span class="hljs-type">UInt</span>])] = <span class="hljs-type">Array</span>(<br>    <span class="hljs-type">SLL</span>   -&gt; rTypeRAlu(sll),<br>    <span class="hljs-type">SRL</span>   -&gt; rTypeRAlu(srl),<br>    <span class="hljs-type">SRA</span>   -&gt; rTypeRAlu(sra),<br><br>    <span class="hljs-type">JR</span>    -&gt; rTypeJr(jumpr),<br><br>    <span class="hljs-type">ADDU</span>  -&gt; rTypeRAlu(add),<br>    <span class="hljs-type">SUBU</span>  -&gt; rTypeRAlu(sub),<br><br>    <span class="hljs-type">AND</span>   -&gt; rTypeRAlu(and),<br>    <span class="hljs-type">OR</span>    -&gt; rTypeRAlu(or),<br>    <span class="hljs-type">XOR</span>   -&gt; rTypeRAlu(xor),<br>    <span class="hljs-type">NOR</span>   -&gt; rTypeRAlu(nor),<br>    <span class="hljs-type">SLT</span>   -&gt; rTypeRAlu(slt),<br>    <span class="hljs-type">SLTU</span>  -&gt; rTypeRAlu(sltu),<br><br>    <span class="hljs-type">JAL</span>   -&gt; jTypeJal(jump),<br><br>    <span class="hljs-type">BEQ</span>   -&gt; iTypeBr(beq),<br>    <span class="hljs-type">BNE</span>   -&gt; iTypeBr(bne),<br><br>    <span class="hljs-type">ADDIU</span> -&gt; iTypeUAlu(add),<br><br>    <span class="hljs-type">LUI</span>   -&gt; iTypeUAlu(lui),<br><br>    <span class="hljs-type">LW</span>    -&gt; iTypeLd(lw),<br><br>    <span class="hljs-type">SW</span>    -&gt; iTypeSt(sw)<br>  )<br>&#125;<br></code></pre></td></tr></table></figure>
<p><code>table</code>
定义了所需要的查找表，它的表项是一系列键-值对，键-值对在 scala 中使用
<code>-&gt;</code>
表示，左边是键，右边是值。在查找表中，所有键的类型都是
<code>BitPat</code>，它是 chisel
中的一种用于做二进制串模式匹配的类型；所有值的类型都是
<code>List[UInt]</code>，即由 <code>UInt</code>
对象组成的列表，用于表示某条指令对应的控制信号的具体取值。</p>
<ol start="2" type="1">
<li>定义控制信号的线束：</li>
</ol>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ControlSignals</span>(<span class="hljs-params">xLen:<span class="hljs-type">Int</span> = 32</span>) <span class="hljs-keyword">extends</span> <span class="hljs-title">Bundle</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> selSrc0 = <span class="hljs-type">UInt</span>()<br>  <span class="hljs-keyword">val</span> selSrc1 = <span class="hljs-type">UInt</span>()<br>  <span class="hljs-keyword">val</span> selImm = <span class="hljs-type">UInt</span>()<br>  <span class="hljs-keyword">val</span> selWnum = <span class="hljs-type">UInt</span>()<br>  <span class="hljs-keyword">val</span> fuType = <span class="hljs-type">UInt</span>()<br>  <span class="hljs-keyword">val</span> fuOp = <span class="hljs-type">UInt</span>()<br>  <span class="hljs-keyword">val</span> rfWen = <span class="hljs-type">Bool</span>()<br>  <span class="hljs-keyword">val</span> illegal = <span class="hljs-type">Bool</span>()<br><br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">signals</span> </span>= <span class="hljs-type">Array</span>(<br>    <span class="hljs-type">MIPS32I</span>.<span class="hljs-type">CtrlIdx</span>.selSrc0 -&gt; selSrc0,<br>    <span class="hljs-type">MIPS32I</span>.<span class="hljs-type">CtrlIdx</span>.selSrc1 -&gt; selSrc1,<br>    <span class="hljs-type">MIPS32I</span>.<span class="hljs-type">CtrlIdx</span>.selImm -&gt; selImm,<br>    <span class="hljs-type">MIPS32I</span>.<span class="hljs-type">CtrlIdx</span>.selWnum -&gt; selWnum,<br>    <span class="hljs-type">MIPS32I</span>.<span class="hljs-type">CtrlIdx</span>.fuType -&gt; fuType,<br>    <span class="hljs-type">MIPS32I</span>.<span class="hljs-type">CtrlIdx</span>.fuOp -&gt; fuOp,<br>    <span class="hljs-type">MIPS32I</span>.<span class="hljs-type">CtrlIdx</span>.rfWen -&gt; rfWen,<br>    <span class="hljs-type">MIPS32I</span>.<span class="hljs-type">CtrlIdx</span>.illegal -&gt; illegal<br>  )<br>&#125;<br></code></pre></td></tr></table></figure>
<p>这里没有指定信号的位宽，在编译时 chisel
将自动推导出它们的宽度。另外，定义了 <code>signal</code>
方法，它返回一个
<code>Array</code>，存储这些信号在查找结果中的索引和具体信号值的键-值对，方便在模块中对其进行迭代。</p>
<ol start="3" type="1">
<li>定义译码模块：</li>
</ol>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Instruction</span>(<span class="hljs-params">xLen:<span class="hljs-type">Int</span> = 32</span>) <span class="hljs-keyword">extends</span> <span class="hljs-title">Bundle</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> instr = <span class="hljs-type">UInt</span>(xLen.<span class="hljs-type">W</span>)<br>  <span class="hljs-keyword">val</span> pc = <span class="hljs-type">UInt</span>(xLen.<span class="hljs-type">W</span>)<br>&#125;<br><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ControlIO</span>(<span class="hljs-params">xLen:<span class="hljs-type">Int</span> = 32</span>) <span class="hljs-keyword">extends</span> <span class="hljs-title">Bundle</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> in = <span class="hljs-type">Input</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">Instruction</span>(xLen))<br>  <span class="hljs-keyword">val</span> out = <span class="hljs-type">Output</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">ControlSignals</span>(xLen))<br>&#125;<br><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Control</span>(<span class="hljs-params">xLen:<span class="hljs-type">Int</span> = 32</span>) <span class="hljs-keyword">extends</span> <span class="hljs-title">Module</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> io = <span class="hljs-type">IO</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">ControlIO</span>(xLen))<br><br>  <span class="hljs-keyword">val</span> ctrlSignals = <span class="hljs-type">ListLookup</span>(io.in.instr, <span class="hljs-type">MIPS32I</span>.<span class="hljs-keyword">default</span>, <span class="hljs-type">MIPS32I</span>.table)<br>  io.out.signals.map(x =&gt; x._2 := ctrlSignals(x._1.id))<br>&#125;<br></code></pre></td></tr></table></figure>
<p>译码模块 <code>Control</code> 的输入是一条指令及其对应的
<code>pc</code>，输出是控制信号。该模块的巧妙之处在于控制信号的生成。</p>
<p>首先，使用 <code>ListLookup</code>
方法对之前构建的查找表进行查找，查找的结果是一个 <code>UInt</code>
组成的 <code>List</code>，将其存储在 <code>ctrlSignals</code> 中。</p>
<p>然后调用 <code>Array</code>（<code>io.out.signals</code> 方法返回一个
<code>Array</code>）的 <code>map</code>
方法进行迭代。该方法传入了一个匿名函数
<code>x =&gt; x._2 := ctrlSignals(x._1.id)</code>，它将
<code>Array</code>中的每一项取出，使用该项的键索引查找结果，然后将其连接到对应的值上。最终的效果是将查找结果的每一项都连接到了对应的控制信号上。</p>
<h3 id="scala-泛型">scala 泛型</h3>
<p>由于 scala 泛型的存在，可以对电路进行更高层次的抽象。</p>
<p>比如说对于流水线寄存器而言，可以将其抽象为：</p>
<ol type="1">
<li>IO 接口：</li>
</ol>
<table>
<thead>
<tr class="header">
<th style="text-align: center;">名称</th>
<th style="text-align: center;">方向</th>
<th style="text-align: center;">说明</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center;">io.in.data.valid</td>
<td style="text-align: center;">input</td>
<td style="text-align: center;">输入数据有效</td>
</tr>
<tr class="even">
<td style="text-align: center;">io.in.data.bits</td>
<td style="text-align: center;">input</td>
<td style="text-align: center;">输入数据</td>
</tr>
<tr class="odd">
<td style="text-align: center;">io.in.stall</td>
<td style="text-align: center;">input</td>
<td style="text-align: center;">流水级暂停</td>
</tr>
<tr class="even">
<td style="text-align: center;">io.in.flush</td>
<td style="text-align: center;">input</td>
<td style="text-align: center;">流水级冲刷</td>
</tr>
<tr class="odd">
<td style="text-align: center;">io.out.data.valid</td>
<td style="text-align: center;">output</td>
<td style="text-align: center;">输出数据有效</td>
</tr>
<tr class="even">
<td style="text-align: center;">io.out.data.bits</td>
<td style="text-align: center;">outptu</td>
<td style="text-align: center;">输出数据</td>
</tr>
</tbody>
</table>
<ol start="2" type="1">
<li>内部逻辑（伪代码）：</li>
</ol>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StageReg</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Module</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> io = <span class="hljs-type">IO</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">StageRegIO</span>)<br><br>  <span class="hljs-keyword">val</span> validReg = <span class="hljs-type">Reg</span>(<span class="hljs-type">Bool</span>())<br>  <span class="hljs-keyword">val</span> dataReg = <span class="hljs-type">Reg</span>(<span class="hljs-type">DataType</span>())<br><br>  when(reset.asBool || io.in.flush) &#123;<br>    validReg := <span class="hljs-number">0.</span><span class="hljs-type">U</span><br>  &#125; .elsewhen(!io.in.stall) &#123;<br>    validReg := io.in.data.valid<br>  &#125;<br><br>  when (!io.in.stall) &#123;<br>    dataReg := io.in.data.bits<br>  &#125;<br><br>  io.out.data.valid := validReg<br>  io.out.data.bits := dataReg<br>&#125;<br></code></pre></td></tr></table></figure>
<p>在实际使用中，IO 接口和内部逻辑都是不变的，发生变化的是输入数据的类型
<code>DataType</code>，因此可以对于输入的数据类型使用泛型：</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StageRegIn</span>[<span class="hljs-type">T</span>&lt;:<span class="hljs-type">Bundle</span>](<span class="hljs-params">xLen:<span class="hljs-type">Int</span> = 32, dataType: <span class="hljs-type">T</span></span>) <span class="hljs-keyword">extends</span> <span class="hljs-title">Bundle</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> stall = <span class="hljs-type">Input</span>(<span class="hljs-type">Bool</span>())<br>  <span class="hljs-keyword">val</span> flush = <span class="hljs-type">Input</span>(<span class="hljs-type">Bool</span>())<br>  <span class="hljs-keyword">val</span> data = <span class="hljs-type">Flipped</span>(<span class="hljs-type">Valid</span>(dataType))<br>&#125;<br><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StageRegOut</span>[<span class="hljs-type">T</span>&lt;:<span class="hljs-type">Bundle</span>](<span class="hljs-params">xLen:<span class="hljs-type">Int</span> = 32, dataType: <span class="hljs-type">T</span></span>) <span class="hljs-keyword">extends</span> <span class="hljs-title">Bundle</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> data = <span class="hljs-type">Valid</span>(dataType)<br>&#125;<br><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StageRegIO</span>[<span class="hljs-type">T</span>&lt;:<span class="hljs-type">Bundle</span>](<span class="hljs-params">xLen:<span class="hljs-type">Int</span> = 32, dataType: <span class="hljs-type">T</span></span>) <span class="hljs-keyword">extends</span> <span class="hljs-title">Bundle</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> in = <span class="hljs-keyword">new</span> <span class="hljs-type">StageRegIn</span>(xLen, dataType)<br>  <span class="hljs-keyword">val</span> out = <span class="hljs-keyword">new</span> <span class="hljs-type">StageRegOut</span>(xLen, dataType)<br>&#125;<br><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StageReg</span>[<span class="hljs-type">T</span>&lt;:<span class="hljs-type">Bundle</span>](<span class="hljs-params">xLen:<span class="hljs-type">Int</span> = 32, dataType: <span class="hljs-type">T</span></span>) <span class="hljs-keyword">extends</span> <span class="hljs-title">Module</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> io = <span class="hljs-type">IO</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">StageRegIO</span>(xLen, dataType))<br><br>  <span class="hljs-keyword">val</span> validReg = <span class="hljs-type">Reg</span>(<span class="hljs-type">Bool</span>())<br>  <span class="hljs-keyword">val</span> dataReg = <span class="hljs-type">Reg</span>(dataType)<br><br>  when(reset.asBool || io.in.flush) &#123;<br>    validReg := <span class="hljs-number">0.</span><span class="hljs-type">U</span><br>  &#125; .elsewhen(!io.in.stall) &#123;<br>    validReg := io.in.data.valid<br>  &#125;<br><br>  when (!io.in.stall) &#123;<br>    dataReg := io.in.data.bits<br>  &#125;<br><br>  io.out.data.valid := validReg<br>  io.out.data.bits := dataReg<br>&#125;<br></code></pre></td></tr></table></figure>
<p>在实例化一个流水级寄存器时，可以传入自定义类型以定制寄存器的输入输出数据类型：</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">val</span> myStageReg = <span class="hljs-type">Module</span>(<span class="hljs-keyword">new</span> <span class="hljs-type">StageReg</span>(<span class="hljs-number">32</span>, <span class="hljs-keyword">new</span> <span class="hljs-type">MyBundle</span>))<br></code></pre></td></tr></table></figure>
<h2 id="chisel-的劣势">chisel 的劣势</h2>
<h3 id="学习成本高">学习成本高</h3>
<p>chisel 是基于 scala 开发的包，因此使用 chisel 需要先学习
scala。相比于 C 而言，scala
是一门完全不同的语言，其中面向对象、函数式编程的特性是 C
所不具备的，需要从头学习。之所以需要学习这些高级特性，是因为只有使用了这些特性才能充分利用
chisel 的优势，否则 chisel 的使用体验和 verilog 差不多。</p>
<h3 id="对工业级开发流程支持尚不完善">对工业级开发流程支持尚不完善</h3>
<p>目前，我只把 chisel 当作一个工具，用于使用更少、更易于维护的代码生成
verilog，然后将 verilog 导入 vivado 工程，作为 SOC
的一部分进行仿真、综合、实现、比特流生成和上板。虽然 chisel
中包含测试模块，但是它完全不支持对于 IP 核的调用。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><p><a
target="_blank" rel="noopener" href="https://www.chisel-lang.org/chisel3/docs/introduction.html">chisel
官方文档</a>：包含了一些 chisel 的基本使用方法</p></li>
<li><p><a
target="_blank" rel="noopener" href="https://www.chisel-lang.org/api/latest/index.html">chisel API
文档</a>：包含所有 chisel API 的原型和使用方法</p></li>
<li><p><a
target="_blank" rel="noopener" href="https://github.com/freechipsproject/chisel-bootcamp">chisel
bootcamp</a>：一个 chisel/scala 从零入门教程</p></li>
</ul>

            </div>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                  <div class="post-meta mr-3">
                    <i class="iconfont icon-category"></i>
                    
                      <a class="hover-with-bg" href="/categories/note/">note</a>
                    
                  </div>
                
                
                  <div class="post-meta">
                    <i class="iconfont icon-tags"></i>
                    
                      <a class="hover-with-bg" href="/tags/%E7%A1%AC%E4%BB%B6%E6%8F%8F%E8%BF%B0%E8%AF%AD%E8%A8%80/">硬件描述语言</a>
                    
                      <a class="hover-with-bg" href="/tags/chisel/">chisel</a>
                    
                  </div>
                
              </div>
              
                <p class="note note-warning">
                  
                    本博客所有文章除特别声明外，均采用 <a target="_blank" href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" rel="nofollow noopener noopener">CC BY-SA 4.0 协议</a> ，转载请注明出处！
                  
                </p>
              
              
                <div class="post-prevnext">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2022/07/21/%E8%AE%B0%E8%AE%A1%E7%AE%97%E6%89%80%E5%A4%8F%E4%BB%A4%E8%90%A5%E7%BB%8F%E5%8E%86/">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">记计算所夏令营经历</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2022/05/02/%E4%BD%BF%E7%94%A8nginx%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86Tomcat/">
                        <span class="hidden-mobile">使用 nginx 在服务器上反向代理 Tomcat</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
              <!-- Comments -->
              <article class="comments" id="comments" lazyload>
                
                  
                
                
  <div id="valine"></div>
  <script type="text/javascript">
    Fluid.utils.loadComments('#valine', function() {
      Fluid.utils.createScript('https://cdn.jsdelivr.net/npm/valine@1/dist/Valine.min.js', function() {
        var options = Object.assign(
          {"appId":"y7tlaLEOoW5C1xBYm8VBo64Q-gzGzoHsz","appKey":"F7gylUefEEtQk24FEbvgfnBi","path":"window.location.pathname","placeholder":"此处留言","avatar":"retro","meta":["nick","mail","link"],"requiredFields":[],"pageSize":10,"lang":"zh-CN","highlight":false,"recordIP":false,"serverURLs":"","emojiCDN":null,"emojiMaps":null,"enableQQ":false},
          {
            el: "#valine",
            path: window.location.pathname
          }
        )
        new Valine(options);
        Fluid.utils.waitElementVisible('#valine .vcontent', () => {
          Fluid.plugins.initFancyBox('#valine .vcontent img:not(.vemoji)');
        })
      });
    });
  </script>
  <noscript>Please enable JavaScript to view the comments</noscript>


              </article>
            
          </article>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->


    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
    

    
  </main>

  <footer class="text-center mt-5 py-3">
  <div class="footer-content">
     <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> && <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
  </div>
  
  <div class="statistics">
    
    

    
      
        <!-- LeanCloud 统计PV -->
        <span id="leancloud-site-pv-container" style="display: none">
            总访问量 
            <span id="leancloud-site-pv"></span>
             次
          </span>
      
      
        <!-- LeanCloud 统计UV -->
        <span id="leancloud-site-uv-container" style="display: none">
            总访客数 
            <span id="leancloud-site-uv"></span>
             人
          </span>
      

    
  </div>


  

  
</footer>


  <!-- SCRIPTS -->
  
  <script  src="https://cdn.jsdelivr.net/npm/nprogress@0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nprogress@0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js" ></script>
<script  src="https://cdn.jsdelivr.net/npm/bootstrap@4/dist/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>

<!-- Plugins -->


  <script  src="/js/local-search.js" ></script>



  
    <script  src="/js/img-lazyload.js" ></script>
  



  



  
    <script  src="https://cdn.jsdelivr.net/npm/tocbot@4/dist/tocbot.min.js" ></script>
  
  
    <script  src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3/dist/jquery.fancybox.min.js" ></script>
  
  
    <script  src="https://cdn.jsdelivr.net/npm/anchor-js@4/anchor.min.js" ></script>
  
  
    <script defer src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js" ></script>
  






  <script  src="https://cdn.jsdelivr.net/npm/typed.js@2/lib/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var title = document.getElementById('subtitle').title;
      
        typing(title);
      
    })(window, document);
  </script>





  

  
    <!-- MathJax -->
    <script>
      MathJax = {
        tex: {
          inlineMath: [['$', '$'], ['\\(', '\\)']]
        },
        loader: {
          load: ['ui/lazy']
        },
        options: {
          renderActions: {
            findScript: [10, doc => {
              document.querySelectorAll('script[type^="math/tex"]').forEach(node => {
                const display = !!node.type.match(/; *mode=display/);
                const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
                const text = document.createTextNode('');
                node.parentNode.replaceChild(text, node);
                math.start = { node: text, delim: '', n: 0 };
                math.end = { node: text, delim: '', n: 0 };
                doc.math.push(math);
              });
            }, '', false],
            insertedScript: [200, () => {
              document.querySelectorAll('mjx-container').forEach(node => {
                let target = node.parentNode;
                if (target.nodeName.toLowerCase() === 'li') {
                  target.parentNode.classList.add('has-jax');
                }
              });
            }, '', false]
          }
        }
      };
    </script>

    <script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js" ></script>

  





  <script  src="https://cdn.jsdelivr.net/npm/mermaid@8/dist/mermaid.min.js" ></script>
  <script>
    if (window.mermaid) {
      mermaid.initialize({"theme":"default"});
    }
  </script>




  

  

  

  

  

  





<!-- 主题的启动项 保持在最底部 -->
<script  src="/js/boot.js" ></script>


<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
        tex2jax: {
            inlineMath: [ ["$","$"], ["\\(","\\)"] ],
            skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'],
            processEscapes: true
        }
    });
    MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax();
        for (var i = 0; i < all.length; ++i)
            all[i].SourceElement().parentNode.className += ' has-jax';
    });
</script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</body>
</html>
